package com.doublethread.easymart.modules.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doublethread.easymart.common.exception.FileException;
import com.doublethread.easymart.common.exception.UserException;
import com.doublethread.easymart.common.result.PageResult;
import com.doublethread.easymart.common.utils.Base64MultipartFile;
import com.doublethread.easymart.common.utils.JwtUtils;
import com.doublethread.easymart.common.utils.OssUtils;
import com.doublethread.easymart.common.utils.PasswordUtils;
import com.doublethread.easymart.modules.user.dto.*;
import com.doublethread.easymart.modules.user.entity.User;
import com.doublethread.easymart.modules.user.mapper.UserMapper;
import com.doublethread.easymart.modules.user.service.UserService;
import com.doublethread.easymart.modules.user.vo.LoginVO;
import com.doublethread.easymart.modules.user.vo.UserStatisticsVO;
import com.doublethread.easymart.modules.user.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private PasswordUtils passwordUtils;

    @Autowired
    private OssUtils ossUtils;

    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public LoginVO login(LoginDTO loginDTO) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, loginDTO.getUsername())
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.invalidCredentials();
        }

        if (!passwordUtils.matches(loginDTO.getPassword(), user.getPassword())) {
            UserException.invalidCredentials();
        }

        if (user.getStatus() != null && user.getStatus().equals(0)) {
            UserException.userDisabled();
        }

        user.setLastLoginTime(java.time.LocalDateTime.now());
        this.updateById(user);

        String token = jwtUtils.generateToken(user.getId(), user.getUsername());

        User updateUser = new User();
        updateUser.setId(user.getId());
        updateUser.setLastLoginTime(LocalDateTime.now());
        this.updateById(updateUser);

        UserVO userVO = convertToUserVO(user);

        return LoginVO.builder()
                .token(token)
                .user(userVO)
                .build();
    }

    @Override
    public void register(RegisterDTO registerDTO) {
        User existingUser = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, registerDTO.getUsername())
                .eq(User::getIsDeleted, 0));
        if (existingUser != null) {
            UserException.usernameExists();
        }

        User existingEmail = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getEmail, registerDTO.getEmail())
                .eq(User::getIsDeleted, 0));
        if (existingEmail != null) {
            UserException.emailExists();
        }

        User newUser = User.builder()
                .username(registerDTO.getUsername())
                .password(passwordUtils.encodePassword(registerDTO.getPassword()))
                .nickname(registerDTO.getNickname())
                .email(registerDTO.getEmail())
                .phone(registerDTO.getPhone())
                .gender(0)
                .status(1)
                .build();

        this.save(newUser);
    }

    @Override
    public UserVO getUserById(Long userId) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        return convertToUserVO(user);
    }

    @Override
    @Transactional
    public void updateUser(Long userId, UserUpdateDTO updateDTO) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        String oldAvatarUrl = user.getAvatar();
        if (updateDTO.getAvatar() != null && updateDTO.getAvatar().startsWith("data:image/")) {
            log.info("上传头像");

            try {
                String newAvatarUrl = uploadAvatar(userId, updateDTO.getAvatar());

                updateDTO.setAvatar(newAvatarUrl);
                log.info("头像上传成功");

                if (oldAvatarUrl != null && !oldAvatarUrl.isEmpty() && oldAvatarUrl.startsWith("http")) {
                    CompletableFuture.runAsync(() -> {
                        try {
                            ossUtils.deleteImage(oldAvatarUrl);
                            log.info("删除旧头像成功");
                        } catch (Exception e) {
                            log.warn("删除旧头像失败");
                        }
                    });
                }

            } catch (Exception e) {
                log.error("头像上传失败");
                updateDTO.setAvatar(oldAvatarUrl);
            }

        } else if (updateDTO.getAvatar() != null && updateDTO.getAvatar().startsWith("http")) {
            log.info("使用URL格式头像");

        } else if (updateDTO.getAvatar() != null && !updateDTO.getAvatar().isEmpty()) {
            log.warn("头像数据格式不正确");
            updateDTO.setAvatar(oldAvatarUrl);
        }

        user.setNickname(updateDTO.getNickname());
        user.setPhone(updateDTO.getPhone());
        user.setAvatar(updateDTO.getAvatar());
        user.setGender(updateDTO.getGender());
        user.setBirthday(updateDTO.getBirthday());

        this.updateById(user);

        log.info("用户信息更新完成");
    }

    @Override
    public PageResult<UserVO> getUserList(UserQueryDTO queryDTO) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0);

        if (StringUtils.hasText(queryDTO.getKeyword())) {
            queryWrapper.and(wrapper -> wrapper
                    .like(User::getUsername, queryDTO.getKeyword())
                    .or().like(User::getNickname, queryDTO.getKeyword())
                    .or().like(User::getEmail, queryDTO.getKeyword())
                    .or().like(User::getPhone, queryDTO.getKeyword())
            );
        }

        if (queryDTO.getStatus() != null) {
            queryWrapper.eq(User::getStatus, queryDTO.getStatus());
        }

        if (queryDTO.getGender() != null) {
            queryWrapper.eq(User::getGender, queryDTO.getGender());
        }

        if (queryDTO.getStartTime() != null) {
            queryWrapper.ge(User::getCreateTime, queryDTO.getStartTime());
        }
        if (queryDTO.getEndTime() != null) {
            queryWrapper.le(User::getCreateTime, queryDTO.getEndTime());
        }

        queryWrapper.orderByDesc(User::getCreateTime);

        Page<User> page = new Page<>(queryDTO.getCurrent(), queryDTO.getSize());
        Page<User> userPage = this.page(page, queryWrapper);

        return PageResult.of(userPage, this::convertToUserVO);
    }

    @Override
    public UserVO getUserDetail(Long userId) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        return convertToUserVO(user);
    }

    @Override
    public void updateUserStatus(Long userId, UserUpdateStatusDTO statusDTO) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        if (userId.equals(1L) && statusDTO.getStatus().equals(0)) {
            UserException.userDisabled();
        }

        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setStatus(statusDTO.getStatus());
        this.updateById(updateUser);
    }

    @Override
    public void deleteUser(Long userId) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        if (userId.equals(1L)) {
            UserException.userDeleted();
        }

        this.removeById(userId);
    }

    @Override
    public UserStatisticsVO getUserStatistics() {
        LocalDateTime now = LocalDateTime.now();
        LocalDate today = now.toLocalDate();
        LocalDate weekStart = today.minusDays(today.getDayOfWeek().getValue() - 1);
        LocalDate monthStart = today.withDayOfMonth(1);

        Long totalUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0));

        Long activeUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .eq(User::getStatus, 1));

        Long disabledUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .eq(User::getStatus, 0));

        Long todayNewUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getCreateTime, today.atStartOfDay())
                .lt(User::getCreateTime, today.plusDays(1).atStartOfDay()));

        Long weekNewUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getCreateTime, weekStart.atStartOfDay())
                .lt(User::getCreateTime, today.plusDays(1).atStartOfDay()));

        Long monthNewUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getCreateTime, monthStart.atStartOfDay())
                .lt(User::getCreateTime, today.plusDays(1).atStartOfDay()));

        Long todayActiveUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getLastLoginTime, today.atStartOfDay())
                .lt(User::getLastLoginTime, today.plusDays(1).atStartOfDay()));

        Long weekActiveUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getLastLoginTime, weekStart.atStartOfDay())
                .lt(User::getLastLoginTime, today.plusDays(1).atStartOfDay()));

        Long monthActiveUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .ge(User::getLastLoginTime, monthStart.atStartOfDay())
                .lt(User::getLastLoginTime, today.plusDays(1).atStartOfDay()));

        Long maleUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .eq(User::getGender, 1));

        Long femaleUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .eq(User::getGender, 2));

        Long unknownGenderUsers = this.count(new LambdaQueryWrapper<User>()
                .eq(User::getIsDeleted, 0)
                .and(wrapper -> wrapper.eq(User::getGender, 0).or().isNull(User::getGender)));

        return UserStatisticsVO.builder()
                .totalUsers(totalUsers)
                .activeUsers(activeUsers)
                .disabledUsers(disabledUsers)
                .todayNewUsers(todayNewUsers)
                .weekNewUsers(weekNewUsers)
                .monthNewUsers(monthNewUsers)
                .todayActiveUsers(todayActiveUsers)
                .weekActiveUsers(weekActiveUsers)
                .monthActiveUsers(monthActiveUsers)
                .maleUsers(maleUsers)
                .femaleUsers(femaleUsers)
                .unknownGenderUsers(unknownGenderUsers)
                .statisticsTime(now)
                .build();
    }

    @Override
    public UserVO getUserProfile(Long userId) {
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        return convertToUserVO(user);
    }

    @Override
    public void updatePassword(Long userId, UserPasswordUpdateDTO passwordUpdateDTO) {
        if (!passwordUpdateDTO.getNewPassword().equals(passwordUpdateDTO.getConfirmPassword())) {
            UserException.passwordMismatch();
        }

        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getId, userId)
                .eq(User::getIsDeleted, 0));

        if (user == null) {
            UserException.userNotFound();
        }

        if (!passwordUtils.matches(passwordUpdateDTO.getOldPassword(), user.getPassword())) {
            UserException.invalidCredentials();
        }

        String encodedNewPassword = passwordUtils.encodePassword(passwordUpdateDTO.getNewPassword());
        User updateUser = new User();
        updateUser.setId(userId);
        updateUser.setPassword(encodedNewPassword);
        this.updateById(updateUser);
    }

    @Override
    public String uploadAvatar(Long userId, String avatarBase64) {
        final String AVATAR_PATH = "images/avatar/";

        try {
            log.info("上传用户头像");

            if (avatarBase64 == null || !avatarBase64.startsWith("data:image/")) {
                log.error("头像数据格式不正确");
                FileException.typeNotAllowed();
            }

            MultipartFile avatarFile = Base64MultipartFile.fromBase64(avatarBase64);

            if (avatarFile.getSize() > 2 * 1024 * 1024) {
                log.error("头像文件过大");
                FileException.sizeExceeded();
            }

            String avatarUrl = ossUtils.uploadImage(avatarFile, AVATAR_PATH);

            if (avatarUrl == null || avatarUrl.isEmpty()) {
                log.error("OSS上传返回空URL");
                FileException.uploadFailed();
            }

            log.info("用户头像上传成功");
            return avatarUrl;

        } catch (Exception e) {
            log.error("用户头像上传失败");
            if (e instanceof RuntimeException) {
                throw e;
            }
            FileException.uploadFailed();
            return null;
        }
    }

    private UserVO convertToUserVO(User user) {
        return UserVO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .email(user.getEmail())
                .phone(user.getPhone())
                .avatar(user.getAvatar())
                .gender(user.getGender())
                .birthday(user.getBirthday())
                .lastLoginTime(user.getLastLoginTime())
                .status(user.getStatus())
                .createTime(user.getCreateTime())
                .updateTime(user.getUpdateTime())
                .build();
    }

}
